package by.dkusch.aircompany.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dkusch.aircompany.dao.CrewDao;
import by.dkusch.aircompany.model.filter.FilterCrew;
import by.dkusch.aircompany.model.filter.base.FilterEntity;
import by.dkusch.aircompany.model.tables.Crew;
import by.dkusch.aircompany.model.tables.Crew_;
import by.dkusch.aircompany.model.tables.Flight;
import by.dkusch.aircompany.model.tables.Flight_;
import by.dkusch.aircompany.model.tables.Person;

@Repository
public class CrewDaoImpl extends AbstractDaoImpl<Long, Crew> implements CrewDao {

	public CrewDaoImpl() {
		super(Crew.class);
	}

	@Override
	public Crew getByIdWithLazyDetails(Long id) {

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		CriteriaQuery<Crew> root = cBuilder.createQuery(Crew.class);
		Root<Crew> criteria = root.from(Crew.class);

		root.select(criteria);
		root.where(cBuilder.equal(criteria.get(Crew_.id), id));
		root.select(criteria);
		criteria.fetch(Crew_.personSet, JoinType.LEFT);
		root.distinct(true);

		TypedQuery<Crew> query = getEm().createQuery(root);
		Crew result = query.getSingleResult();
		return result;

	}

	@Override
	public Set<Person> getPersonSet(Long id) {
		if (id != null) {
			Crew crew = getByIdWithLazyDetails(id);
			if (crew != null) {
				return crew.getPersonSet();
			}
		}
		return new TreeSet<Person>();
	}

	@Override
	public List<Crew> getAllItemsWithFilter(SingularAttribute<? super Crew, ?> attr, boolean ascending, int startRecord, int pageSize, FilterEntity<Long, ? super Crew> filter) {
		if (filter == null) {
			return getAllItems(attr, ascending, startRecord, pageSize);
		}

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Crew> criteria = cBuilder.createQuery(Crew.class);
		Root<Crew> root = criteria.from(Crew.class);

		criteria.select(root).distinct(true);
		if (attr != null) {
			criteria.orderBy(new OrderImpl(root.get(attr), ascending));
		}

		List<Predicate> conditions = new ArrayList<Predicate>();

		FilterCrew fc = (FilterCrew) filter;
		if (fc.getCode() != null) {
			conditions.add(cBuilder.equal(cBuilder.upper(root.get(Crew_.code)), fc.getCode().toUpperCase()));
		}

		if (fc.getName() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(root.get(Crew_.name)), fc.getName().toUpperCase()));
		}

		if ((fc.getStartDateMin() != null) && (fc.getStartDateMax() != null)) {
			conditions.add(cBuilder.between(root.get(Crew_.startDate), fc.getStartDateMin(), fc.getStartDateMax()));
		} else if ((fc.getStartDateMin() != null) && (fc.getStartDateMax() == null)) {
			conditions.add(cBuilder.greaterThanOrEqualTo(root.get(Crew_.startDate), fc.getStartDateMin()));
		} else if ((fc.getStartDateMin() == null) && (fc.getStartDateMax() != null)) {
			conditions.add(cBuilder.lessThanOrEqualTo(root.get(Crew_.startDate), fc.getStartDateMax()));
		}

		if ((fc.getEndDateMin() != null) && (fc.getEndDateMax() != null)) {
			conditions.add(cBuilder.between(root.get(Crew_.startDate), fc.getEndDateMin(), fc.getEndDateMax()));
		} else if ((fc.getEndDateMin() != null) && (fc.getEndDateMax() == null)) {
			conditions.add(cBuilder.greaterThanOrEqualTo(root.get(Crew_.startDate), fc.getEndDateMin()));
		} else if ((fc.getEndDateMin() == null) && (fc.getEndDateMax() != null)) {
			conditions.add(cBuilder.lessThanOrEqualTo(root.get(Crew_.startDate), fc.getEndDateMax()));
		}

		criteria.where(cBuilder.and(conditions.toArray(new Predicate[] {})));

		TypedQuery<Crew> query = getEm().createQuery(criteria);
		if (startRecord >= 0) {
			query.setFirstResult(startRecord);
			query.setMaxResults(pageSize);
		}
		List<Crew> results = query.getResultList();
		return results;

	}

	@Override
	public boolean checkCanRemoveRecord(Crew entity) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		CriteriaQuery<Flight> criteriaFlight = cBuilder.createQuery(Flight.class);

		Root<Flight> fromFlight = criteriaFlight.from(Flight.class);
		Join<Flight, Crew> fromFlight2Crew = fromFlight.join(Flight_.crew);

		List<Predicate> conditions = new ArrayList<Predicate>();
		conditions.add(cBuilder.equal(fromFlight2Crew.get(Crew_.id), entity.getId()));

		TypedQuery<Flight> query = getEm().createQuery(criteriaFlight.select(fromFlight).where(cBuilder.and(conditions.toArray(new Predicate[] {}))));
		List<Flight> results = query.getResultList();
		boolean flightsEmpty = (results == null) || ((results != null) && (results.size() == 0));
		if (!flightsEmpty) {
			return false;
		} else {
			Set<Person> persons = getPersonSet(entity.getId());
			return (persons == null) || ((persons != null) && (persons.size() == 0));
		}
	}

}
